home *** CD-ROM | disk | FTP | other *** search
-
- - ASMVLA01 - File I/O - 04/14/93 -
-
- Lately we have been quite busy with school, so this second issue is a
- little behind schedule. But that's life... This little issue will quickly
- show off the DOS file functions: read, write, open, close, create & others.
- They are all pretty much the same, so there isn't a whole lot to go over.
- But, as a bonus, I'm going to throw in a bit about how to do a subroutine.
- Let's do the subroutine stuff first.
-
- `Procedures' as they are called, are declared like this:
-
- ────────────────────────────────────────────────────────────────────────────
-
- PROC TheProcedure
-
- ... ;do whatever..
-
- ret ;MUST have a RET statement!
- ENDP TheProcedure
-
- ────────────────────────────────────────────────────────────────────────────
-
- In the procedure, you can do basically anything you want, just at the
- end of it, you say ret. You can also specify how to call the PROC by putting
- a NEAR or FAR after the procedure name. This tells the compiler whether
- to change segment AND offset, or just offset when the procedure is called.
- Note that if you don't specify, it compiles into whatever the default is for
- the current .MODEL (small = near, large = far)
-
- ────────────────────────────────────────────────────────────────────────────
-
- PROC TheProc NEAR
-
- ...
-
- ret ;this compiles to `retn' (return near- pops offset off
- ENDP TheProc ; stack only)
-
- OR
-
- PROC TheProc FAR
-
- ...
-
- ret ;compiles to `retf' pops both offset & segment off stack
- ENDP TheProc ; pops offset first
-
- ────────────────────────────────────────────────────────────────────────────
-
- That's basically all there is to that. Note that if you REALLY wanted to
- be tricky, you could do a far jump by doing this:
-
- ────────────────────────────────────────────────────────────────────────────
- push seg TheProc
- push offset TheProc
- retf
- ────────────────────────────────────────────────────────────────────────────
-
- This would "return" you to the beginning of the procedure "TheProc"...
- This code is just to illustrate a point. If you actually did something like
- this and compiled and executed it, it would bomb. Know why? What happens
- when it hits the `ret' in the PROC? Well it pops off the offset and puts
- it in IP and then pops the segment and puts it in CS. Who knows what was
- on the stack... will return to an unknown address and probably crash. (It
- DEFINATELY will not continue executing your code.)
-
- Of course, the only stack operations are PUSH and POP. All they do is
- push or pop off the stack a word sized or a Dword sized piece of data. NEVER
- under ANY circumstance try to push a byte sized piece of data! The results
- are unpredictable. Well, not really, but just don't do it, ok?
-
- There are also two commands that'll save you some time and code space:
-
- PUSHA and POPA (push all and Pop all)
-
- PUSHA pushes the general registers in this order:
-
- AX, CX, DX, BX, SP, BP, SI, DI
-
- POPA pops the general registers in this order:
-
- DI, SI, BP, (sp), BX, DX, CX, AX
-
- SP is different because popa does NOT restore the value of SP. It merely
- pops it off and throws it away.
-
- For the 386+, pushad and popad push and pop all extended registers in
- the same order. You don't need to memorize the order, because you don't
- need to know the order until you go and get tricky. (hint: the location of
- AX on the stack is [sp + 14] - useful if you want to change what AX returns,
- but you did a pusha cause you wanted to save all the registers (except AX)
- Then you'd do a popa, and AX= whatever value you put in there.
-
- ────
-
- Alright, now a slightly different topic: memory management
-
- Ok, this isn't true by-the-book memory management, but you need to know
- one thing: Upon execution of a program, DOS gives it ALL memory up to the
- address A000:0000. This happens to be the beginning of the VGA buffer...
- Another thing you must know is that, if you used DOSSEG at the top of your
- file, the segment is the last piece of your program. The size of the segment
- is derived from the little command `STACK 200h' or whatever the value was
- that you put up there. The 200h is the number of bytes in the stack. To get
- the number of paragraphs, you'd divide by 16. Here's an example of how I can
- get a pointer to the first valid available segment that I can use for data:
-
- ────────────────────────────────────────────────────────────────────────────
- mov ax,ss ;grab the stack segment
- add ax,200h/16 ;add the size of the stack 200h/16 = 20h
-
- ;AX now contains the value of the first available segment the you can
- ; use.
- ────────────────────────────────────────────────────────────────────────────
-
- This is very nice, because you can just plop your data right there
- and you have a 64k buffer you can use for anything you want.
-
- Ok, say you want to find out how much memory is available to use. This
- would be done like this: (no suprises, I hope.)
-
- ────────────────────────────────────────────────────────────────────────────
- mov ax,ss ;grab the stack segment
- add ax,200h/16 ;add the size of the stack 200h/16 = 20h
- mov bx,0A000h ;upper limit of the free memory
- sub bx,ax ;bx= # of paragraphs available
- ────────────────────────────────────────────────────────────────────────────
-
- Pretty darn simple. That's enough of the overhead that you must know
- to understand the included ANSI viewer (asm3.asm)
-
- Now to the FILE I/O stuff...
-
- Files can be opened, read from, written to, created, and closed. To open
- a file, all you need to do is give the DOS interrupt a name & path. All
- references to that file are done through what's known as a file handle. A
- file handle is simply a 16bit integer that DOS uses to identify the file.
- It's used more or less like an index into chart of pointers that point to
- a big structure that holds all the info about the file- like current position
- in the file, file type, etc.. all the data needed to maintain a file.
- The `FILES= 20' thing in your autoexec simply tells DOS how much memory to
- grab for those structures. ( Files=20 grabs enough room for 20 open files. )
-
- ANYway, here's each of the important function calls and a rundown on what
- they do and how to work them.
-
- ────────────────────────────────────────────────────────────────────────────
- FILE OPEN: Function 3Dh
-
- IN:
- ah= 3Dh
- al= open mode
-
- bits 7-3: Stuff that doesn't matter to us
- bits 2-0: Access code
- 000 read only access
- 001 write only access
- 010 read and write access
-
- DS:DX= pointer to the ASCIIZ filename
- ASCIIZ means that its an ASCII string with a Zero on the end.
-
- Returns:
- CF=1 error occured
- AX= error code- don't worry about what they are, if the carry
- is set, you didn't open the file.
-
- CF=0 no error
- AX= File Handle ;you need to keep this- it's your only way to
- ; reference your file!
-
- ──── EXAMPLE ────
-
- [...] ;header stuff
-
- .CODE ;this stuff is used for all the examples
-
- FileName db "TextFile.TXT",0
- FileHandle dw 0
- Buffer db 300 dup (0)
- BytesRead dw 0
- FileSize dd 0
-
- [...] ;more stuff
-
- mov ax,3d00h ; open file for read only
- mov ax,cs
- mov ds,ax ;we use CS, cause it's pointing to the CODE segment
- ; and our file name is in the code segment
- mov dx,offset FileName
- int 21h
- jc FileError_Open
-
- mov [FileHandle],ax
-
- [...] ;etc...
-
- ────────────────────────────────────────────────────────────────────────────
- FILE CLOSE: Function 3Eh
-
- IN:
- AH= 3Eh
- BX= File Handle
-
- RETURN:
- CF=1 error occured, but who cares?
-
- ──── EXAMPLE ────
-
- mov bx,[FileHandle]
- mov ah,3eh
- int 21h
-
- ────────────────────────────────────────────────────────────────────────────
- FILE READ: Function 3Fh
-
- IN:
- AH= 3Fh
- BX= File Handle
- CX= Number of bytes to read
- DS:DX= where to put data that is read from the file (in memory)
-
- RETURN:
- AX= number of bytes actually read- if 0, then you tried to read from
- the end of the file.
-
- ──── EXAMPLE ────
-
- mov bx,[FileHandle]
- mov ax,cs
- mov ds,ax
- mov dx,offset buffer
- mov ah,3Fh
- mov cx,300
- int 21h
-
- mov [BytesRead],ax
-
- ────────────────────────────────────────────────────────────────────────────
- FILE WRITE: Function 40h
-
- IN:
- AH= 40h
- BX= File Handle
- CX= Number of bytes to write
- DS:DX= where to read data from (in memory) to put on disk
-
- RETURN:
- AX= number of bytes actually written- if not equal to the number of bytes
- that you wanted to write, you have an error.
-
- ──── EXAMPLE ────
-
- mov bx,[FileHandle]
- mov ax,cs
- mov ds,ax
- mov dx,offset buffer
- mov ah,40h
- mov cx,[BytesRead]
- int 21h
-
- cmp cx,ax
- jne FileError_Write
-
- ────────────────────────────────────────────────────────────────────────────
- FILE CREATE: Function 3Ch
-
- IN:
- ah= 3Ch
- cl= file attribute
-
- bit 0: read-only
- bit 1: hidden
- bit 2: system
- bit 3: volume label
- bit 4: sub directory
- bit 5: Archive
- bit 6&7: reserved
-
- DS:DX= pointer to the ASCIIZ filename
- ASCIIZ means that its an ASCII string with a Zero on the end.
-
- Returns:
- CF=1 error occured
- AX= error code- don't worry about what they are, if CF
- is set, you didn't create the file.
-
- CF=0 no error
- AX= File Handle ;you need to keep this- it's your only way to
- ; reference your file!
- ──── EXAMPLE ────
-
- mov ah,3ch
- mov ax,cs
- mov ds,ax ;we use CS, cause it's pointing to the CODE segment
- ; and our file name is in the code segment
- mov dx,offset FileName
- mov cx,0 ;no attributes
- int 21h
- jc FileError_Create
-
- mov [FileHandle],ax
-
- ────────────────────────────────────────────────────────────────────────────
- FILE DELETE: Function 41h
-
- IN:
- ah= 41h
- DS:DX= pointer to the ASCIIZ filename
-
- Returns:
- CF=1 error occured
- AX= error code- 2= file not found, 3= path not found
- 5= access denied
-
- CF=0 no error
-
- ──── EXAMPLE ────
-
- mov ah,41h ;kill the sucker
- mov ax,cs
- mov ds,ax
- mov dx,offset FileName
- int 21h
- jc FileError_Delete
-
- ────────────────────────────────────────────────────────────────────────────
- FILE MOVE POINTER: Function 42h
-
- IN:
- ah= 42h
- BX= File Handle
- CX:DX= 32 bit pointer to location in file to move to
- AL= 0 offset from beginning of file
- = 1 offset from curent position
- = 2 offset from the end of the file
-
- Returns:
- CF=1 error occured
- AX= error code- no move occured
-
- CF=0 no error
- DX:AX 32 bit pointer to indicate current location in file
-
- ──── EXAMPLE ────
-
- mov ah,42h ;find out the size of the file
- mov bx,[FileHandle]
- xor cx,cx
- xor dx,dx
- mov al,2
- int 21h
-
- mov [word low FileSize],ax
- mov [word high FileSize],dx ;load data into filesize
-
- (or in MASM mode,
-
- mov word ptr [FileSize],ax
- mov word ptr [FileSize+2],dx
-
- need I say why I like Ideal mode? )
-
- ────────────────────────────────────────────────────────────────────────────
- FILE CHANGE MODE: Function 43h
-
- IN:
- ah= 43h
- DS:DX= pointer to the ASCIIZ filename
- al= 0
- returns file attributes in CX
- al= 1
- sets file attributes to what's in CX
-
- Returns:
- CF=1 error occured
- AX= error code- 2= file not found, 3= path not found.
- 5= access denied
-
- CF=0 no error
-
- ──── EXAMPLE ──── Lets erase a hidden file in your root directory...
-
- FileName db "C:\msdos.sys",0
-
- [...]
-
- mov ah,43h ;change attribute to that of a normal file
- mov ax,cs
- mov ds,ax
- mov dx,offset FileName
- mov al,1 ;set to whats in CX
- mov cx,0 ;attribute = 0
- int 21h
-
- mov ah,41h ;Nuke it with the delete command
- int 21h
-
- ────────────────────────────────────────────────────────────────────────────
-
- Well, that's all for now. I hope this info is enough for you to do some
- SERIOUS damage... :) I just don't want to see any 'bombs' running around
- erasing the hidden files in the root directory, ok?
-
- Anyway, go take a look at asm3.asm- it's a SIMPLE ansi/text displayer.
- It just opens the file, reads it all into a "buffer" that was "allocated"
- immediatly after the stack & reads in the entire file (if it's < 64k) and
- prints out the file character by character via DOS's print char (fn# 2).
- Very simple and very slow. You'd need a better print routine to go faster...
- The quickest display programs would decode the ANSI on its own... But that's
- kinda a chore... Oh, well. Enjoy.
-
- Draeden/VLA
-
-
- Suggested projects:
-
- 1) Write a program that will try to open a file, but if it does not
- find it, the program creates the file and fills it with a simple
- text message.
-
- 2) Write a program that will input your keystrokes and write them
- directly to a text file.
-
- 3) The write & read routines actually can be used for a file or device.
- Try to figure out what the FileHandle for the text screen is by
- writing to the device with various file handles. This same channel,
- when read from, takes it's data from the keyboard. Try to read data
- from the keyboard. Maybe read like 20 characters... CTRL-Z is the
- end of file marker.
-
- 4) Try to use a file as `virtual memory'- open it for read/write access
- and write stuff to it and then read it back again after moving the
- cursor position.
-
-
-
-